<!DOCTYPE html>
<html lang="zh-cn">

<head>
  <meta charset="utf-8">
  <title>个人账户信息管理</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>

<body>
  <div class="hero is-fullheight" x-data="app" x-init="mounted">

    <!-- 首页 -->
    <div x-show="page == 'home'" class="container-fluid">
      <nav class="level is-mobile p-3 mb-2 has-background-success-light">
        <!-- <div class="level-left"> -->
          <div class="level-item">
            <p class="subtitle is-5 has-text-weight-bold">个人账户信息管理</p>
          </div>
        <!-- </div> -->

        <div class="level-right">
          <p class="level-item"><a class="button is-small is-info is-rounded" @click="logout">退出登录</a></p>
        </div>
      </nav>

      <nav class="level is-mobile">
        <div class="level-left ml-4">
          <input x-model="findStr" @keyup.enter="search"
                class="input is-primary is-small is-rounded" type="text"
                placeholder="标题/账号/备注" autofocus="autofocus" x-ref="search" />
          <button @click="search" class="button is-info is-small">搜索</button>
        </div>
      </nav>

        <table class="table is-striped is-fullwidth" style="word-break:break-all; word-wrap:break-all;">
          <template x-if="records.length">
            <thead>
              <tr>
                <th>标题</th>
                <th>用户名</th>
                <th>密码</th>
                <th>网址</th>
              </tr>
              <tr><th colspan="4" style="text-align: center;">备注</th></tr>
            </thead>
          </template>
          <template x-for="(rec, idx) in records" :key="rec.id">
            <tbody>
              <tr>
                <td class="has-text-primary-dark" x-text="rec.title"></td>
                <td class="has-text-warning-dark" x-text="rec.user"></td>
                <td class="has-text-danger-dark" x-text="rec.pass"></td>
                <td class="has-text-link" x-text="rec.url"></td>
              </tr>
              <template x-if="rec.notes != ''">
                <tr>
                  <td colspan="4"><pre style="word-wrap: break-word; white-space: pre-wrap;" x-text="rec.notes"></pre></td>
                </tr>
              </template>
            </tbody>
          </template>
        </table>
    </div>
    <!-- 首页结束 -->

    <!-- 登录页 -->
    <div class="hero-body is-justify-content-center is-align-items-center" x-show="page == 'login'">
      <div class="columns is-flex is-flex-direction-column box">
        <div class="has-text-centered">
          <h6 class="text-center has-text-primary-dark has-text-weight-bold">个人账户信息管理</h6>
        </div>
        <div class="column">
          <input autofocus class="input is-primary" type="text" placeholder="用户名" x-model="username"
            x-ref="user" @keyup.enter="$refs.pwd.focus()">
          <div class="has-text-danger is-size-7" x-show="reqUser">必须输入用户名</div>
        </div>
        <div class="column">
          <input class="input is-primary" type="password" placeholder="密码" x-model="password"
            x-ref="pwd" @keyup.enter="login">
          <div class="has-text-danger is-size-7" x-show="reqPass">必须输入密码</div>
        </div>
        <div class="column">
          <button class="button is-primary is-fullwidth" type="submit" @click="login">登录</button>
        </div>
      </div>
    </div>
    <!-- 登录页结束 -->

  </div>


  <script>
    const ACCESS_TOKEN_NAME = "access_token"

    async function apiPost(url, body, token, callback) {
        const headers = {'Content-Type': 'application/json'}
        if (token)
          headers['Authorization'] = 'session ' + token
        if (body)
          body = JSON.stringify(body)

        const rep = await fetch(url, { headers, body, method: 'POST' })
        const json = await rep.json()
        if (json.code != 200)
          window.alert(json.message)
        else
          callback(json.data)
    }

    var app = {
      page: 'login',
      token: null,

      // home page
      findStr: '',
      records: [],

      // login page
      username: null,
      password: null,
      reqUser: false,
      reqPass: false,

      mounted: function() {
        if (this.getToken())
          this.page = "home"
        else
          this.$refs.user.focus()
      },

      login: function () {
        // 输入校验
        this.reqUser = !this.username;
        this.reqPass = !this.password;
        if (this.reqUser || this.reqPass) return;
          apiPost('/api/login', {user: this.username, pass: this.password}, null, (res) => {
            this.setToken(res.token, res.expire)
            this.username = null
            this.password = null
            this.page = 'home'
          });
      },

      // 退出登录
      logout: function () {
        apiPost("/api/logout", null, this.getToken(), (res) => {});
        this.token = null;
        window.sessionStorage.removeItem(ACCESS_TOKEN_NAME);
        this.reqUser = false,
        this.reqPass = false,
        this.records = []
        this.findStr = ''
        this.page = 'login'
      },

      // 查找
      search: function () {
        const q = this.findStr.trim();
        apiPost("/api/list", {q}, this.getToken(), (res) => {
          this.records = res.records
        })
      },

      // 保存token到sessionStorage
      setToken: function (token, exp) {
        this.token = {
          accessToken: token,
          exp: new Date(exp),
        }
        window.sessionStorage.setItem(ACCESS_TOKEN_NAME, JSON.stringify(this.token));
      },

      // 从sessionStorage中获取token
      getToken: function () {
        if (!this.token) {
          const tokenStr = window.sessionStorage.getItem(ACCESS_TOKEN_NAME)
          if (tokenStr) {
            token = JSON.parse(tokenStr)
            if (token && token.accessToken && token.exp)
              token.exp = new Date(token.exp)
              this.token = token
          }
        }

        const now = new Date()

        if (!this.token || !this.token.accessToken || this.token.exp < now)
          return null

        return this.token.accessToken
      },
    }

  </script>
</body>

</html>
